package com.cmcc.waf.report.feign;

import cn.hutool.core.util.HexUtil;
import com.cmcc.waf.report.utils.OpSignatureUtil;
import feign.Logger;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.UUID;

@Slf4j
public class FeignConfig {
    private static final String FEIGN_SIGNATURE_HEADER = "X-Feign-Signature";

    @Value("${waf.openapi.accessKey}")
    private String accessKey;
    @Value("${waf.openapi.secretKey}")
    private String secretKey;


    @Bean
    Logger.Level feignLoggerLevel() {
        //这里记录所有，根据实际情况选择合适的日志level，此处取值共有四个NONE、BASIC、HEADERS和FULL
        return Logger.Level.FULL;
    }

    @Bean
    public DefaultInterceptor defaultInterceptor() {
        return new DefaultInterceptor(accessKey, secretKey);
    }

    static class DefaultInterceptor implements RequestInterceptor {
        private static String accessKey;
        private static String secretKey;

        public DefaultInterceptor(String accessKey, String secretKey) {
            this.accessKey = accessKey;
            this.secretKey = secretKey;
        }

        @Override
        public void apply(RequestTemplate template) {
            // 避免重复签名
            if (template.headers().containsKey(FEIGN_SIGNATURE_HEADER)) {
                log.debug("已经被OP签名，不再处理");
                return;
            }
            template.header("Content-Type", "application/json");
            template.header(FEIGN_SIGNATURE_HEADER, "true");

            OpSignatureUtil.getSignature(template, accessKey, secretKey);
//
//            try {
//                // 获取HTTP方法和路径
//                String httpMethod = template.method();
//                String servletPath = template.url();
//
//                // 获取并处理参数
//                Map<String, Object> parameters = template.queries()
//                        .entrySet().stream()
//                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
//
//                // 添加时间戳、随机数等必要参数
//                addRequiredParams(parameters);
//
//                // 对参数进行排序和编码
//                String canonicalizedQueryString = buildCanonicalizedQueryString(parameters);
//
//                // 构建待签名字符串
//                String stringToSign = buildStringToSign(httpMethod, servletPath, canonicalizedQueryString);
//
//                // 生成签名
//                String signature = generateSignature(SECRET_KEY, stringToSign);
//
//                // 设置签名到请求头部
//                template.header(FEIGN_SIGNATURE_HEADER, signature);
//
//                // 更新模板参数（如果有新增的）
//                parameters.forEach((key, value) -> template.query(key, value.toString()));
//            } catch (Exception e) {
//                log.error("Failed to sign the request", e);
//            }
        }

        private void addRequiredParams(Map<String, Object> parameters, String ACCESS_KEY) {
            parameters.putIfAbsent("AccessKey", ACCESS_KEY);
            parameters.putIfAbsent("Timestamp", formatCurrentTime());
            parameters.putIfAbsent("SignatureMethod", "HmacSHA1");
            parameters.putIfAbsent("SignatureNonce", UUID.randomUUID().toString());
            parameters.putIfAbsent("SignatureVersion", "V2.0");
        }

        private String formatCurrentTime() {
            return new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US)
                    .format(new java.util.Date());
        }

        private String buildCanonicalizedQueryString(Map<String, Object> parameters) throws Exception {
            StringBuilder queryString = new StringBuilder();
            parameters.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .forEach(entry -> {
                        try {
                            queryString.append(percentEncode(entry.getKey()))
                                    .append("=")
                                    .append(percentEncode(entry.getValue().toString()))
                                    .append("&");
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
            if (queryString.length() > 0) {
                queryString.setLength(queryString.length() - 1); // 移除最后一个&
            }
            return queryString.toString();
        }

        private String buildStringToSign(String httpMethod, String servletPath, String queryString) throws Exception {
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
            return String.format("%s\n%s\n%s", httpMethod, percentEncode(servletPath), bytesToHex(sha256.digest(queryString.getBytes(StandardCharsets.UTF_8))));
        }

        private String generateSignature(String secretKey, String data) throws NoSuchAlgorithmException, InvalidKeyException {

            try {
                Mac mac = Mac.getInstance("HmacSHA1");
                byte[] secretKeyByte = secretKey.getBytes("utf-8");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeyByte, "HmacSHA1");
                mac.init(secretKeySpec);
                return bytesToHex(mac.doFinal(data.getBytes("utf-8")));
            } catch (InvalidKeyException | UnsupportedEncodingException | NoSuchAlgorithmException var5) {
                return null;
            }
        }

        private String percentEncode(String value) throws Exception {
            return value != null ? URLEncoder.encode(value, "utf-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;

        }

        private String bytesToHex(byte[] bytes) {
            return HexUtil.encodeHexStr(bytes);
        }
    }

}
